docs: Guidance for higher-level SDKs to consume MSAL mTLS PoP - #933
docs: Guidance for higher-level SDKs to consume MSAL mTLS PoP#933Gladwin Johnson (gladjohn) wants to merge 2 commits into
Conversation
Explains how azure-identity and other SDKs can integrate with the MSI v2 mTLS Proof-of-Possession API. Covers: - Public API surface and return value contract - WindowsCertificate object (accurate property names) - Step-by-step integration pattern (credential -> transport) - SchannelSession usage (cert in constructor, not per-request) - No-fallback behavior matching MSAL .NET - End-user DX goal (zero mTLS awareness) - .NET comparison table - Minimum integration example - Future OpenSSL 3 CNG Provider path Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new documentation page intended to guide higher-level Azure SDKs in consuming MSAL Python’s Managed Identity v2 mTLS Proof-of-Possession (PoP) integration pattern (credential → transport) and related return-value/typing contracts.
Changes:
- Introduces a new guidance document describing an mTLS PoP consumption model for higher-level SDKs.
- Provides sample code for credential integration, auth header construction, and SChannel-based transport usage.
- Documents intended API contracts (parameters, return keys, and key object types) for the integration.
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- Add prerequisite note: APIs come from PR #931, not yet on dev - Note this is standalone docs (not Sphinx-rendered) - Fix rstrip('/.default') -> removesuffix('/.default') - Fix AccessToken usage: store token_type on credential (not AccessToken) - Fix auth policy: read token_type from credential, not token object Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
|
From Azure SDK POV:
|
|
|
||
| ## Future: OpenSSL 3 CNG Provider (Strategic Path) | ||
|
|
||
| When a Microsoft-supported OpenSSL 3 CNG Provider becomes available, the |
There was a problem hiding this comment.
I want to flag that this "strategic path," as written, isn't reachable today and likely not for a long while. I checked with the Python core devs on whether hardware-key mTLS would become reachable from the stdlib ssl module / SSLContext , and the answer was "not coming any time soon."
As y'all know, ssl.SSLContext has no way to reference a provider/PKCS#11 key, load_cert_chain only accepts key material as files, and there's no signing-callback or provider-key hook. So even if a Microsoft-supported OpenSSL 3 CNG provider existed, requests/aiohttp still couldn't use it, because both sit on stdlib ssl, which can't point at a provider-backed key by thumbprint.
The snippet in this section ( create_mtls_context(thumbprint=...) → plain requests.get(...) ) would require exactly the CPython feature that doesn't exist.
The proper channel for this is a new TLS module (the PEP 543 / tlslib effort), and its native-backend work (incl. SChannel) is currently stalled for lack of contributors https://discuss.python.org/t/pre-pep-discussion-revival-of-pep-543-a-unified-tls-api-for-python/51263
They are looking for people who can drive this however :)
| ```python | ||
| from azure.core.pipeline.transport import HttpTransport | ||
|
|
||
| class SchannelTransport(HttpTransport): |
There was a problem hiding this comment.
This particular Schannel Transport will basically have to mirror the functionality provided by requests and aiohttp ( for async ) . You will have to create a full WinHTTP based HTTP Client making sure we get the following features out from it :
- request/response serialization and a conformant azure.core.rest.HttpResponse (streaming/ iter_bytes / read /encoding) -> for folks who are using storage, cosmos, etc etc that have different needs from a http client
- connection pooling / keep-alive (the sketch opens a fresh mTLS handshake per request)
- redirects, proxies, timeouts, retries, TLS config, and error→exception mapping — all of which WinHTTP does differently than requests/aiohttp, so they'd have to be reconciled to keep behavior consistent across platforms
- a separate async transport entirely, since aiohttp can't present the key and WinHTTP here is sync
A few thoughts on differences -
- does the transport need to worry about differences between Windows versions for eg
- does winhttp support reading certs in the same manner as requests/aiohttp ?
- Will standard proxy env vars ( HTTP_PROXY / HTTPS_PROXY / NO_PROXY ) be honored
- Diagnostics diverge too:
urllib3 / aiohttpdebug logging (and standard support steps) won't work on WinHTTP — its tracing lives in Windows ETW — and policy-level logs may not reflect WinHTTP's internal redirects/headers.
Just this itself greatly increases the effort and maintenance cost and the security concerns around this too
| credential = ManagedIdentityCredential(mtls_pop=True) | ||
|
|
||
| # Standard SDK usage — no mTLS awareness needed by the developer | ||
| client = SecretClient( |
There was a problem hiding this comment.
credential and transport are separate in azure-core. So doesn't SecretClient still need updating to hand the credential to a WinHTTP transport and use it
Summary
Adds a single documentation file explaining how higher-level SDKs (e.g., azure-identity, azure-sdk-for-python) can integrate with the MSI v2 mTLS Proof-of-Possession API.
This PR contains only documentation — no code changes.
Contents
WindowsCertificateobject properties and methods (accurate to implementation)SchannelSessionusage (certificate in constructor, not per-request)MtlsPopTokenNotSupportedinImdsV1)Related
GetManagedIdentityCapabilitiesAsync)